//
// Created by LiuYou on 2021/11/25.
//

#include <iostream>
#include <csignal>
#include <arpa/inet.h>

#include "../Config/Config.hpp"
#include "../ThreadPool/ThreadPool.hpp"
#include "../Task/HttpConn/HttpConn.hpp"

/*
 * 172.28.169.17 6677
 * 测试1: http://120.25.220.231:6677/SilentHill2/brief.html
 * 使用firefox和chrome都测试没有问题。
 */
int main(int argc, char** argv) {
    if (argc <= 2) {
        fprintf( stderr, "用法: %s ip地址 端口号\n", basename( argv[0] ));
        return 1;
    }


//    daemonize();


    // 不加也是没有问题的。但是还是加上吧。
    struct sigaction sa;
    sa.sa_handler = SIG_IGN;
    sa.sa_flags |= SA_RESTART;
    sigfillset( &sa.sa_mask );
    sigaction( SIGPIPE, &sa, nullptr );


    const char* const ip = argv[1];
    int port = atoi( argv[2] );


    ThreadPool<HttpConn>* pool = nullptr;
    try {
        pool = new ThreadPool<HttpConn>;
    } catch (...) {
        return 1;
    }


    // todo 笔记: 每次调试这里都要等好久。
    HttpConn* users = new HttpConn[65536];
    int userCount = 0;

    int listenFd = socket( AF_INET, SOCK_STREAM, 0 );

    struct linger tmp = {1, 0};
    setsockopt( listenFd, SOL_SOCKET, SO_LINGER, &tmp, sizeof( tmp ));

    sockaddr_in localAddress{};
    localAddress.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &localAddress.sin_addr );
    localAddress.sin_port = htons( port );

    bind( listenFd, (struct sockaddr*) &localAddress, sizeof( localAddress ));

    listen( listenFd, 5 );

    epoll_event epollEvents[10000];
    int epollFd = epoll_create( 5 );

    Epoll::addFdToEpoll( epollFd, listenFd, false );
    HttpConn::epollFd = epollFd;

    while (true) {
        int readyFds = epoll_wait( epollFd, epollEvents, 10000, -1 );
        if ((readyFds < 0) && (errno != EINTR)) {
            fprintf( stderr, "epoll_wait();调用失败。\n" );
            break;
        }

        for (int i = 0; i < readyFds; ++i) {

            int readyFd = epollEvents[i].data.fd;

            if (readyFd == listenFd) {
                sockaddr_in clientAddress{};
                socklen_t clientAddressLen = sizeof( clientAddress );
                int connFd = accept( listenFd, (sockaddr*) &clientAddress, &clientAddressLen );
                if (connFd < 0) {
                    fprintf( stderr, "accept(): %s.\n", strerror(errno));
                    continue;
                }
                if (HttpConn::userCount >= 65536) {
                    fprintf( stdout, "网络繁忙\n" );
                    continue;
                }

                fprintf( stdout, "一个新的 http 连接到来。\n" );
                fprintf( stdout, "HttpConn对象的 connFd 为: %d。\n", connFd );


                users[connFd].initConn( connFd, clientAddress );
                fprintf( stdout, "当前有 %d 个用户。\n", HttpConn::userCount );
            } else if (epollEvents[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {

                users[readyFd].closeConn();

            } else if (epollEvents[i].events & EPOLLIN) {

                if (users[readyFd].read()) {
                    pool->appendTaskObj( users + readyFd );
                } else {

                    users[readyFd].closeConn();

                }
            } else if (epollEvents[i].events & EPOLLOUT) {

                if (!users[readyFd].write()) {

                    users[readyFd].closeConn();

                }
            } else {

            }

        }

    }

    close( epollFd );
    close( listenFd );
    delete[] users;
    delete pool;
    return 0;
}
